#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <linux/device.h>
#include <linux/cdev.h>

#include "global_define.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lijun");
MODULE_DESCRIPTION("Deiver that to get cnt of process in which state");

/* 
 * task id defined in include/linux/sched.h *
 *
 * #define TASK_RUNNING        0
 * #define TASK_INTERRUPTIBLE  1
 * #define TASK_UNINTERRUPTIBLE    2
 * #define __TASK_STOPPED      4
 * 
 */

static int major_num;
static struct class* _class = NULL;
static struct device* _device = NULL;

#define DEVICE_NAME "pid_state_cnt"
#define CLASS_NAME "pid_state_cnt_class"


static int get_pid_state_cnt(int state) {
	struct task_struct * p;
	read_lock(&tasklist_lock);
	int cnt = 0;
	for_each_process(p){
		if(p->state & state) {
			cnt ++;
		}
	}
	read_unlock(&tasklist_lock);
	return cnt;
}

static long _ioctl( struct file * file,
					unsigned int cmd,
					unsigned long param) {
	int retval = 0;
	MSG msg;
	switch(cmd) {
		case PID_STATE_CNT_GET:
			{
				printk(KERN_INFO "[get_pid_state:] Enter PID_STATE_CNT_GET .\n");
				retval = copy_from_user(&msg, (struct msg __user *)param, sizeof(struct msg));
				if(retval) return -EFAULT;
				msg.cnt = get_pid_state_cnt(msg.state);
				printk(KERN_INFO "[get_pid_state:] state:%d cnt:%d .\n", msg.state,msg.cnt);
				retval = copy_to_user((unsigned int __user*)param, &msg, sizeof(msg));
				if(retval) return -EFAULT;
				break;
			}
		default:
			retval= -EINVAL;
			break;
	}
	return retval;
}

static const struct file_operations opt = {
	.owner = THIS_MODULE,
	.unlocked_ioctl = _ioctl,
};

static int __init _init(void){
	printk(KERN_INFO "[get_pid_state:] Enter get_pid_state module.\n");

	major_num = register_chrdev(0, DEVICE_NAME, &opt);
	if(major_num < 0) {
		printk(KERN_INFO "[get_pid_state:] Failed to register a major number.\n");
		return major_num;
	}
	printk(KERN_INFO "[get_pid_state:] Successful to register a major number %d.\n",major_num);

	_class = class_create(THIS_MODULE, CLASS_NAME);
	if(IS_ERR(_class)) {
		unregister_chrdev(major_num, DEVICE_NAME);
		printk(KERN_INFO "[get_pid_state:] Class device register failed.\n");
		return PTR_ERR(_class);
	}
	printk(KERN_INFO "[get_pid_state:] Class device register success.\n");

	_device = device_create(_class, NULL, MKDEV(major_num, 0), NULL, DEVICE_NAME);
	if(IS_ERR(_device)) {
		class_destroy(_class);
		unregister_chrdev(major_num, DEVICE_NAME);
		printk(KERN_INFO "[get_pid_state:] Failed to create the device.\n");
		return PTR_ERR(_device);
	}
	printk(KERN_INFO "[get_pid_state:] get_pid_state module register successful.\n");
	return 0;
}

static void __exit _exit(void){
	printk(KERN_INFO "[get_pid_state:] Start to clean up module.\n");
	device_destroy(_class, MKDEV(major_num, 0));
	class_destroy(_class);
	unregister_chrdev(major_num, DEVICE_NAME);
	printk(KERN_INFO "[get_pid_state:] Clean up successful.Bye\n");
}

module_init(_init);
module_exit(_exit);
